<template>
  <McLayout class="container">
    <McHeader :title="'智能导购助手'" :logoImg="'https://matechat.gitcode.com/logo.svg'">
      <template #operationArea>
        <div class="operations">
          <i class="icon-helping"></i>
        </div>
      </template>
    </McHeader>
    <McLayoutContent
      v-if="startPage"
      style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px"
    >
      <McIntroduction
        :logoImg="'https://matechat.gitcode.com/logo2x.svg'"
        :title="'智能导购助手'"
        :subTitle="'Hi，我是小智，您的专属购物顾问'"
        :description="description"
      ></McIntroduction>
      <McPrompt
        :list="introPrompt.list"
        :direction="introPrompt.direction"
        class="intro-prompt"
        @itemClick="onSubmit($event.label)"
      ></McPrompt>
    </McLayoutContent>
    <McLayoutContent class="content-container" v-else>
      <template v-for="(msg, idx) in messages" :key="idx">
        <McBubble
          v-if="msg.from === 'user'"
          :content="msg.content"
          :align="'right'"
          :avatarConfig="{ imgSrc: 'https://matechat.gitcode.com/png/demo/userAvatar.svg' }"
        >
        </McBubble>
        <McBubble v-else :avatarConfig="{ imgSrc: 'https://matechat.gitcode.com/logo.svg' }" :loading="msg.loading">
          <template #default>
            <div class="markdown-content" v-html="renderMarkdown(msg.displayContent || msg.content)"></div>
            <ProductCard v-if="msg.products && msg.products.length > 0" :products="msg.products" />
          </template>
        </McBubble>
      </template>
    </McLayoutContent>
    <div class="shortcut" style="display: flex; align-items: center; gap: 8px">
      <McPrompt
        v-if="!startPage"
        :list="simplePrompt"
        :direction="'horizontal'"
        style="flex: 1"
        @itemClick="onSubmit($event.label)"
      ></McPrompt>
      <Button
        v-if="isGenerating"
        color="danger"
        variant="outline"
        icon="op-stop"
        title="停止回答"
        size="md"
        @click="stopGeneration"
      >
        停止回答
      </Button>
      <Button
        style="margin-left: auto"
        icon="add"
        shape="circle"
        title="新建对话"
        size="md"
        @click="newConversation"
      />
    </div>
    <McLayoutSender>
      <McInput :value="inputValue" :maxLength="2000" :disabled="isGenerating" @change="(e: string) => (inputValue = e)" @submit="onSubmit">
        <template #extra>
          <div class="input-foot-wrapper">
            <div class="input-foot-left">
              <span v-for="(item, index) in inputFootIcons" :key="index">
                <i :class="item.icon"></i>
                {{ item.text }}
              </span>
              <span class="input-foot-dividing-line"></span>
              <span class="input-foot-maxlength">{{ inputValue.length }}/2000</span>
            </div>
            <div class="input-foot-right">
              <Button icon="op-clearup" shape="round" :disabled="!inputValue" @click="inputValue = ''"><span class="demo-button-content">清空输入</span></Button>
            </div>
          </div>
        </template>
      </McInput>
    </McLayoutSender>
  </McLayout>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Button } from 'vue-devui/button';
import 'vue-devui/button/style.css';
import OpenAI from 'openai';
import { modelConfig } from './config/model.config';
import { renderMarkdown } from './utils/markdown';
import 'highlight.js/styles/github.css'; // 导入代码高亮样式
import ProductCard from './components/ProductCard.vue';
import { parseProducts, removeProductJson } from './types/product';

// 初始化 OpenAI 客户端
const client = new OpenAI({
  apiKey: modelConfig.apiKey, // 模型 APIKey
  baseURL: modelConfig.baseURL, // 模型 API 地址
  dangerouslyAllowBrowser: true,
});

const description = [
  '智能导购助手可以根据您的需求，为您推荐合适的商品。',
  '告诉我您想要什么类型的商品，我会为您精心挑选并展示详细信息。',
];
const introPrompt = {
  direction: 'horizontal',
  list: [
    {
      value: 'laptop',
      label: '推荐一款适合办公的笔记本',
      iconConfig: { name: 'icon-info-o', color: '#5e7ce0' },
      desc: '根据办公需求推荐合适的笔记本电脑',
    },
    {
      value: 'phone',
      label: '推荐性价比高的手机',
      iconConfig: { name: 'icon-star', color: 'rgb(255, 215, 0)' },
      desc: '为您推荐性价比出色的智能手机',
    },
    {
      value: 'headphone',
      label: '推荐降噪耳机',
      iconConfig: { name: 'icon-priority', color: '#3ac295' },
      desc: '推荐降噪效果好的耳机',
    },
  ],
};
const simplePrompt = [
  {
    value: 'laptop',
    iconConfig: { name: 'icon-info-o', color: '#5e7ce0' },
    label: '推荐笔记本',
  },
  {
    value: 'phone',
    iconConfig: { name: 'icon-star', color: 'rgb(255, 215, 0)' },
    label: '推荐手机',
  },
];
const startPage = ref(true);
const inputValue = ref('');
const inputFootIcons = [
  { icon: 'icon-at', text: '智能体' },
  { icon: 'icon-standard', text: '词库' },
  { icon: 'icon-add', text: '附件' },
];

const messages = ref<any[]>([]);
const isGenerating = ref(false); // 是否正在生成回答
const abortController = ref<AbortController | null>(null); // 用于中断请求

const newConversation = () => {
  startPage.value = true;
  messages.value = [];
  isGenerating.value = false;
  if (abortController.value) {
    abortController.value.abort();
    abortController.value = null;
  }
}

const onSubmit = (evt: string) => {
  // 如果正在生成，不允许提交新问题
  if (isGenerating.value) {
    return;
  }
  
  inputValue.value = '';
  startPage.value = false;
  // 用户发送消息
  messages.value.push({
    from: 'user',
    content: evt,
  });

  fetchData(evt);
};

const stopGeneration = () => {
  if (abortController.value) {
    abortController.value.abort();
    abortController.value = null;
  }
  isGenerating.value = false;
  
  // 更新最后一条消息的状态
  if (messages.value.length > 0) {
    const lastMsg = messages.value[messages.value.length - 1];
    if (lastMsg.from === 'model') {
      lastMsg.loading = false;
      if (!lastMsg.content) {
        lastMsg.content = '回答已停止。';
      }
    }
  }
};

const fetchData = async (ques: string) => {
  // 创建新的 AbortController
  abortController.value = new AbortController();
  isGenerating.value = true;
  
  messages.value.push({
    from: 'model',
    content: '',
    displayContent: '',
    products: [],
    id: '',
    loading: true,
  });
  
  try {
    const completion = await client.chat.completions.create({
      model: modelConfig.model, // 使用配置文件中的模型名称
      messages: [
        { 
          role: 'system', 
          content: `你是一个专业的智能导购助手，名叫"小智"。你的任务是根据用户需求推荐合适的商品。

当推荐商品时，请按照以下格式返回：
1. 先用自然语言介绍推荐的商品和理由
2. 然后在回答末尾添加商品数据，格式如下：

\`\`\`json
[
  {
    "name": "商品名称",
    "price": 价格数字,
    "image": "图片URL",
    "desc": "简短描述（20字以内）"
  }
]
\`\`\`

注意：
- 每次推荐2-3个商品
- 价格要真实合理
- 图片必须使用以下格式之一：
  * 笔记本电脑: https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=400
  * 手机: https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=400
  * 耳机: https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400
  * 平板: https://images.unsplash.com/photo-1561154464-82e9adf32764?w=400
  * 键盘: https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=400
  * 鼠标: https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=400
  * 显示器: https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=400
  * 音箱: https://images.unsplash.com/photo-1608043152269-423dbba4e7e1?w=400
  * 相机: https://images.unsplash.com/photo-1502920917128-1aa500764cbd?w=400
  * 手表: https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400
- 根据商品类型选择对应的图片URL
- 描述要简洁突出卖点` 
        },
        { role: 'user', content: ques }
      ],
      stream: true, // 为 true 则开启接口的流式返回
    }, {
      signal: abortController.value.signal, // 传入 abort signal
    });
    
    for await (const chunk of completion) {
      // 检查是否被中断
      if (!isGenerating.value) {
        break;
      }
      
      messages.value[messages.value.length - 1].loading = false;
      const content = chunk.choices[0]?.delta?.content || '';
      const chatId = chunk.id;
      const lastMsg = messages.value[messages.value.length - 1];
      
      // 累积完整内容
      lastMsg.content += content;
      lastMsg.id = chatId;
      
      // 实时移除 JSON 部分，只显示文字
      lastMsg.displayContent = removeProductJson(lastMsg.content);
      
      // 实时解析商品数据（如果 JSON 已完整）
      const products = parseProducts(lastMsg.content);
      if (products.length > 0) {
        lastMsg.products = products;
      }
    }
    
    // 完成后重置状态
    isGenerating.value = false;
    abortController.value = null;
  } catch (error: any) {
    messages.value[messages.value.length - 1].loading = false;
    
    // 区分是用户中断还是真实错误
    if (error.name === 'AbortError' || error.message?.includes('aborted')) {
      // 用户主动停止，不显示错误信息
      if (!messages.value[messages.value.length - 1].content) {
        messages.value[messages.value.length - 1].content = '回答已停止。';
      }
    } else {
      messages.value[messages.value.length - 1].content = '抱歉，请求失败，请检查 API 配置或网络连接。';
      console.error('API 调用失败:', error);
    }
    
    isGenerating.value = false;
    abortController.value = null;
  }
};
</script>

<style>
.container {
  width: 1000px;
  margin: 20px auto;
  height: calc(100vh - 82px);
  padding: 20px;
  gap: 8px;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 16px;
}

.content-container {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: auto;
}

.shortcut {
  display: flex;
  align-items: center;
  gap: 8px;
}

.shortcut .devui-btn {
  white-space: nowrap;
}

.input-foot-wrapper {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 100%;
  margin-right: 8px;

  .input-foot-left {
    display: flex;
    align-items: center;
    gap: 8px;

    span {
      font-size: 14px;
      line-height: 18px;
      color: #252b3a;
      cursor: pointer;
    }

    .input-foot-dividing-line {
      width: 1px;
      height: 14px;
      background-color: #d7d8da;
    }

    .input-foot-maxlength {
      font-size: 14px;
      color: #71757f;
    }
  }

  .input-foot-right {
    .demo-button-content {
      font-size: 14px;
    }

    & > *:not(:first-child) {
      margin-left: 8px;
    }
  }
}

/* Markdown 内容样式 */
.markdown-content {
  line-height: 1.6;
  color: #252b3a;
  word-wrap: break-word;
}

.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
  margin-top: 16px;
  margin-bottom: 8px;
  font-weight: 600;
  line-height: 1.25;
}

.markdown-content h1 {
  font-size: 2em;
  border-bottom: 1px solid #e1e4e8;
  padding-bottom: 8px;
}

.markdown-content h2 {
  font-size: 1.5em;
  border-bottom: 1px solid #e1e4e8;
  padding-bottom: 8px;
}

.markdown-content h3 {
  font-size: 1.25em;
}

.markdown-content p {
  margin-top: 0;
  margin-bottom: 10px;
}

.markdown-content code {
  padding: 2px 6px;
  margin: 0 2px;
  font-size: 90%;
  background-color: rgba(27, 31, 35, 0.05);
  border-radius: 3px;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}

.markdown-content pre {
  padding: 16px;
  overflow: auto;
  font-size: 85%;
  line-height: 1.45;
  background-color: #f6f8fa;
  border-radius: 6px;
  margin-top: 0;
  margin-bottom: 16px;
}

.markdown-content pre code {
  display: inline;
  padding: 0;
  margin: 0;
  overflow: visible;
  line-height: inherit;
  word-wrap: normal;
  background-color: transparent;
  border: 0;
}

.markdown-content ul,
.markdown-content ol {
  padding-left: 2em;
  margin-top: 0;
  margin-bottom: 16px;
}

.markdown-content li {
  margin-bottom: 4px;
}

.markdown-content li > p {
  margin-bottom: 0;
}

.markdown-content blockquote {
  padding: 0 1em;
  color: #57606a;
  border-left: 4px solid #d0d7de;
  margin: 0 0 16px 0;
}

.markdown-content table {
  border-spacing: 0;
  border-collapse: collapse;
  margin-top: 0;
  margin-bottom: 16px;
  width: 100%;
  overflow: auto;
}

.markdown-content table th {
  font-weight: 600;
  padding: 6px 13px;
  border: 1px solid #d0d7de;
  background-color: #f6f8fa;
}

.markdown-content table td {
  padding: 6px 13px;
  border: 1px solid #d0d7de;
}

.markdown-content table tr {
  background-color: #fff;
  border-top: 1px solid #d0d7de;
}

.markdown-content table tr:nth-child(2n) {
  background-color: #f6f8fa;
}

.markdown-content a {
  color: #0969da;
  text-decoration: none;
}

.markdown-content a:hover {
  text-decoration: underline;
}

.markdown-content img {
  max-width: 100%;
  box-sizing: content-box;
  background-color: #fff;
}

.markdown-content hr {
  height: 0.25em;
  padding: 0;
  margin: 24px 0;
  background-color: #d0d7de;
  border: 0;
}
</style>